package com.yjt.zdjf.standard.util;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.serializer.Deserializer;
import org.springframework.core.serializer.Serializer;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.ValueFilter;
import com.yjt.zdjf.standard.common.constant.ConstantValues;
import com.yjt.zdjf.standard.model.ZDJFMessage;
import com.yjt.zdjf.standard.model.common.Head;
import com.yjt.zdjf.standard.model.zdcx.ZDCXRspMessage;

/**
 * 主动缴费作为客户端的标准报文序列化器
 * @author lizebin
 *
 */
@Component(ZDJFClientSerializerDeserializer.PACKAGE_CLASS_NAME)
public class ZDJFClientSerializerDeserializer implements Serializer<ZDJFMessage>,
		Deserializer<ZDJFMessage> {

	public static final String PACKAGE_CLASS_NAME = "com.yjt.zdjf.standard.util.ZDJFClientSerializerDeserializer";
	
	protected final Logger logger = LoggerFactory.getLogger(ZDJFClientSerializerDeserializer.class);
	
	/**
	 * JSON序列化过滤器：替换BODY的value
	 * */
	private final ValueFilter valueFilterForBody = new ValueFilter() {
		@Override
		public Object process(Object object, String name, Object value) {
			if (name.equals(ConstantValues.JSON_TAG_NAME_BODY)) {
                return encrypt(JSON.toJSONString(value));
            }
            return value;
		}
	};

	@Override
	public ZDJFMessage deserialize(InputStream is) throws IOException {
		/*0.字节流转化成字符串*/
		String messageJSONString = null;
		try {
			messageJSONString = inputStreamToString(is);
		} catch(Exception e) {
			logger.error("[zdjf client deserialize]遇到异常，无法处理报文，直接丢弃。异常抛出，服务端socket连接会关闭，客户端socket无法感知，直到超时", e);
			throw e;
		}
		/*1.报文必填要素检查*/
		//{"HEAD":"...", "BODY":"..."} 转换成 Map
		JSONObject messageMap = JSON.parseObject(messageJSONString, JSONObject.class);
		if(!checkMessageElement(messageMap)) {
			//FIXME:lizb 报文要素检查不通过
		}
		
		/*2.head JSON字符串转化成Head*/
		String headJSONString = messageMap.get(ConstantValues.JSON_TAG_NAME_HEAD).toString();
		Head head = JSON.parseObject(headJSONString, Head.class);
		//对应的交易种类
		final String txnType = head.getTxnType();
		//设置内部流转的流水号，此流水号全局唯一，作为唯一键索引
		head.setInnerMsgId(UUID.randomUUID().toString().replaceAll("\\-", ""));
		
		/*3.body JSON字符串转化成ZDJFMessage.Body，暂定对body进行加解密*/
		String bodyJSONString = decrypt(messageMap.get(ConstantValues.JSON_TAG_NAME_BODY).toString());
		
		/*4.组装目标交易种类报文*/
		ZDJFMessage targetMessage = null;
		if(txnType.equals(ConstantValues.MESSAGE_HEADER_TXNTYPE_ZDCX)) {
			targetMessage = new ZDCXRspMessage();
			targetMessage.setHead(head);
			targetMessage.setBody(JSON.parseObject(bodyJSONString, ZDCXRspMessage.Body.class));
		}
		if(logger.isInfoEnabled()) {
			logger.info("[zdjf client deserialize]原JSON字符串[{'targetRequest':{}}]", JSON.toJSONString(targetMessage));
		}
		return targetMessage;
	}

	@Override
	public void serialize(ZDJFMessage request, OutputStream os) throws IOException {
		/*0.加密Body部分*/
		String zdjfMessageJSONString = JSON.toJSONString(request, valueFilterForBody);
		/*1.JSON字符串回应报文序列化*/
		//报文的字节长度
		int zdjfMessageJSONStringLength = zdjfMessageJSONString.getBytes(ConstantValues.MESSAGE_CHARSET).length;
		//起始字节符
		os.write(ConstantValues.STX);
		//8位报文长度，十进制表示，不足左补零
		os.write(StringUtils.leftPad(String.valueOf(zdjfMessageJSONStringLength), ConstantValues.MESSAGE_LENGTH_DIGIT, '0').getBytes(ConstantValues.MESSAGE_CHARSET));
		//报文体
		os.write(zdjfMessageJSONString.getBytes(ConstantValues.MESSAGE_CHARSET));
		//结束字节符
		os.write(ConstantValues.ETX);
		os.flush();
		logger.info("[zdjf client serialize]发送到服务端:" + zdjfMessageJSONString);
	}
	
	protected void checkClosure(int bite) throws IOException {
		if (bite < 0) {
			throw new IOException("Socket 连接已关闭，无法读取或输出数据");
		}
	}
	
	public String inputStreamToString(InputStream is) throws IOException, IllegalArgumentException {
		int bite = is.read();
		if (bite < 0) {
			logger.error("[zdjf client deserialize]报文为空，报文丢弃不处理");
			throw new IOException("报文为空，报文丢弃不处理");
		}
		if (bite != ConstantValues.STX) {
			logger.error("[zdjf client deserialize]非字节{}开头，报文被丢弃", "0x02");
			throw new IOException("非字节0x02开头，报文被丢弃");
		}
		String messageBodyString = null;
		//解析报文长度：报文长度为8位
		byte[] buffer = new byte[ConstantValues.MESSAGE_LENGTH_DIGIT];
		ByteArrayOutputStream byteArrayOutputStreamByteLength = new ByteArrayOutputStream(ConstantValues.MESSAGE_LENGTH_DIGIT);
		if (-1 != is.read(buffer)) {
			byteArrayOutputStreamByteLength.write(buffer);
		} else {
			logger.error("[zdjf client deserialize]报文长度不足8位");
			throw new IOException("报文长度不足8位");
		}
		int messageLength = 0;
		try {
			messageLength = Integer.parseInt(byteArrayOutputStreamByteLength.toString(ConstantValues.MESSAGE_CHARSET));
		} catch(Exception e) {
			logger.error("[zdjf client deserialize]报文长度包含非法字符，只能包含数字，丢弃报文{'报文字节长度':{}}", byteArrayOutputStreamByteLength.toString(ConstantValues.MESSAGE_CHARSET));
			throw new IllegalArgumentException("报文长度包含非法字符，只能包含数字，丢弃报文");
		}
		//解析报文体，防止数组拷贝扩容效率降低
		ByteArrayOutputStream byteArrayOutputStreamMessage = new ByteArrayOutputStream(messageLength);
		int byteLength = 0;
		while (ConstantValues.ETX != (bite = is.read())) {
			checkClosure(bite);
			if(++byteLength > messageLength) {
				logger.error("[zdjf client deserialize]报文实际字节长度不符，实际报文字节长度大于目标报文字节长度");
				throw new IOException("报文实际字节长度不符，实际报文字节长度大于目标报文字节长度");
			}
			byteArrayOutputStreamMessage.write(bite);
		}
		int length = byteArrayOutputStreamMessage.toByteArray().length;
		/*检查报文是否完整*/
		if(messageLength > length) {
			logger.error("[zdjf client deserialize]报文实际字节长度不符{'实际字节长度':{}, '目标字节长度':{}}", String.valueOf(length), String.valueOf(messageLength));
			throw new IOException("报文实际字节长度不符，实际报文字节长度小于目标报文字节长度");
		}
		messageBodyString = byteArrayOutputStreamMessage.toString(ConstantValues.MESSAGE_CHARSET);
		return messageBodyString;
	}
	
	/**
	 * 报文加密
	 * @param body
	 * @return
	 */
	public String encrypt(String bodyJSONString) {
		return bodyJSONString;
	}
	
	
	/**
	 * 报文解密
	 * @param body
	 * @return
	 */
	public String decrypt(String bodyPlainTextString) {
		return bodyPlainTextString;
	}
	
	/**
	 * 检查报文元素
	 * @param requestMap
	 * @return
	 */
	public boolean checkMessageElement(JSONObject requestMap) {
		if(!requestMap.containsKey(ConstantValues.JSON_TAG_NAME_HEAD)) {
			logger.error("[zdjf client deserialize]原始JSON字符串报文不包含head标签[原字符串:{}]", requestMap);
			return false;
		}
		
		if(!requestMap.containsKey(ConstantValues.JSON_TAG_NAME_BODY)) {
			logger.error("[zdjf client deserialize]原始JSON字符串报文不包含body标签[原字符串:{}]", requestMap);
			return false;
		}
		return true;
	}
}